var FTPUtils = (function (FTPUtils) {

  function isInt(s) {
    return s.match(/^\d+$/) !== null;
  }

  /**
  * getEntryDate - if valid arguments passed in, constructs and returns date, 
  * otherwise null is returned
  * @param {string} month 3-letters defining the month 
  * @param {string} day 
  * @param {string} rest this is either the year of the time in the (H)H:(M)M 
  *     format
  * @return {string or null} returns the constructed date or null if valid 
  *     argument were not passed in.
  */
  FTPUtils.getEntryDate = function(month, day, rest) {
    month = month.toLowerCase();
    if (month.length < 3 || !isInt(day)) {
      return null;
    }
    else if (month.length > 3) {
      month = month.slice(0,3);
    }
    var months = {
    "jan": 0, "feb": 1, "mar": 2, "apr": 3, "may": 4, "jun": 5, "jul": 6,
    "aug": 7, "sep": 8, "oct": 9, "nov": 10, "dec": 11
    };
    var monthInt = null;
    for (var key in months) {
      if (key == month) {
        monthInt = months[month];
        break;
      }
    }
    if (monthInt === null) {
      return null;
    }
    var year, hours, minutes;
    var currentDate = new Date();
    if (!isInt(rest)) {
      // Maybe it's time. Time can be any of theformats "HH:MM", "H:MM", "HH:M",
      // "H:M"
      if (rest.length > 5) {
        return null;
      }
      var timeComponents = rest.split(":");
      if (timeComponents.length != 2) {
        return null;
      }
      hours = timeComponents[0];
      minutes = timeComponents[1];
      if (!isInt(hours) || !isInt(minutes)) {
        return null;
      }
      // since the year was not sent, get the current year from user's local date 
      year = currentDate.getFullYear();
      if (monthInt > currentDate.getMonth() || monthInt == currentDate.getMonth() &&
        day > currentDate.getDay()) {
        year--;
      }
    }
    else {
      year = rest;
      hours = currentDate.getHours();
      minutes = currentDate.getMinutes();
    }
    var date = new Date(year, monthInt, day, hours, minutes).toLocaleString();
    return date;
  };

  /**
  * getISO08601date - if valid arguments passed in, constructs and returns date, 
  * otherwise null is returned
  * @param {string} date The format of date must be YYYY-MM-DD
  * @param {string} time The format of time must be (H)H:(M)M 
  * @return {string or null} returns the constructed date or null if valid 
  *     argument were not passed in.
  */
  FTPUtils.getISO8601date = function(date, time) {
    var dateComponents = date.split("-");
    if (dateComponents.length != 3) {
      return null;
    }
    if (!isInt(dateComponents[0]) || !isInt(dateComponents[1]) ||
        !isInt(dateComponents[2])) {
      return null;
    }
    var timeComponents = time.split(":");
    if (timeComponents.length != 2) {
      return null;
    }
    if (!isInt(timeComponents[0]) || !isInt(timeComponents[1])) {
      return null;
    }
    var entryDate = new Date(dateComponents[0], dateComponents[1],
      dateComponents[2], timeComponents[0], timeComponents[1]).toLocaleString();
    return entryDate;
  };

  /**
   * detectDateAndDateOffset - given an array containing the components of a
   * unix line ls output, detects the array offset of date and then construct
   * and returns the date
   * These are the components that a listing line should contain. # indicates
   * a required field:
   *  # 1. permission listing
   *    2. number of links (optional)
   *  # 3. owner name (may contain spaces)
   *    4. group name (optional, may contain spaces)
   *  # 5. size in bytes
   *  # 6. month
   *  # 7. day of month
   *  # 8. year or time <-- dateOffset will be the index of this component
   *    9. file name (optional, may contain spaces)
   */
  FTPUtils.detectDateAndDateOffset = function(entryComponents) {
    var date, dateOffset;
    for (var j = 5; j < entryComponents.length; j++) {
      date = FTPUtils.getEntryDate(entryComponents[j-2], entryComponents[j-1], entryComponents[j]);
      if (date) {
        dateOffset = j;
        break;
      }
    }
    if (!date) {
      // some FTP servers have swapped the "month" and "day of month" colums
      // if none of the combination above worked, we try to recognize these 
      // server
      for (j = 5; j < entryComponents.length; j++) {
        date = FTPUtils.getEntryDate(entryComponents[j-1], entryComponents[j-2], entryComponents[j]);
        if (date) {
          dateOffset = j;
          break;
        }
      }
      // still haven't been able to get the date. Some FTP servers use the ISO  
      // 8601 date format. Try to recognize those servers
      if (!date) {
        for (j = 5; j < entryComponents.length; j++) {
          date = FTPUtils.getISO8601date(entryComponents[j-1], entryComponents[j]);
          if (date) {
            dateOffset = j;
            break;
          }
        }
        if (!date) {
          return null;
        }
      }
    }
    return {"date": date, "dateOffset": dateOffset};
  };

  /**
  * getEntryName - given an array containing the components of a unix line ls 
  * output, and the array offset of date, constructs the entry name. In the
  * array, the entry name component is immediately preceded by date, hence the
  * date offset is passed in. If the entry name contained spaces, then the array
  * @param {array} entryComponents The components making up a single line from 
  *    the ls -l output
  * @param {string} entry A single line from the ls -l output 
  * @param {number} dateOffset the offset of date into the entrycomponents array
  * @return {string} returns the entry Name
  */
  FTPUtils.getEntryName = function(entryComponents, entry, dateOffset) {
    var correctNameComponents = entryComponents.slice(dateOffset+1),
      firstComponent = entryComponents[dateOffset+1],
      searchOffset = 0,
      nameOffset = 0,
      searchedComponents = [], entryName;
    while (nameOffset != -1 &&
           searchedComponents.toString() != correctNameComponents.toString()) {
      nameOffset = entry.indexOf(firstComponent, searchOffset);
      entryName = entry.slice(nameOffset);
      searchedComponents = entryName.split(/\s+/);
      searchOffset = nameOffset + firstComponent.length;
    }
    if (nameOffset == -1) {
      return "";
    }
    if (entryName.search("\r\n") != -1) {
      entryName = entryName.slice(0, entryName.length-2);
    }
    else if (entryName.search("\r") != -1 || entryName.search("\n") != -1) {
      entryName = entryName.slice(0, entryName.length-1);
    }
    return entryName;
  };

  /** 
   * parseLsDirectoryListing - given a string that contains unix-formated 
   * directory listing, for each entry, returns an object containing entry
   * information such as entry name, last modified date, entry size, persmissions.
   * Assuming a directory dontains n entries, the format would look as following:
   * <entry 1 line> <CRLF>
     <entry 2 line> <CRLF>
     <entry 3 line> <CRLF>
     ...
     <entry n line> <CRLF>, where each of these entry lines is composed of 6 
     required fields and three optional fields.
     <entry k line> :: = <permissions> <number of links (opt)> <owner name>
                         <group name (opt)> <size> <month> <day of month> 
                         <year or time> <file name (opt)>
   *@param {string} lsOutput
   *@return {array} returns an array of objects, where each object k contains 
   *    information about entry k  
  */
  FTPUtils.parseLsDirectoryListing = function(lsOutput) {
    var unixLines = lsOutput.split(/[\r\n]+/g);
    if (unixLines[unixLines.length-1] == "") {
      unixLines.pop();
    }
    var entriesInfo = [];
    for (var i = 0; i < unixLines.length; i++) {
      var entry = unixLines[i];
      var entryComponents = entry.split(/\s+/);
      var result = FTPUtils.detectDateAndDateOffset(entryComponents);
      if (!result) {
        return null;
      }
      var date = result.date, dateOffset = result.dateOffset;
      var size = entryComponents[dateOffset-3];
      var entryName = FTPUtils.getEntryName(entryComponents, entry, dateOffset);
      var permissions = entryComponents[0], isLink, isFile;
      if (permissions.search(/^d/) === 0) {
        entryName += "/";
        isFile = false;
        isLink = false;
      }
      else if (permissions.search(/^l/) === 0) {
        isLink = true;
        isFile = false;
      }
      else {
        isFile = true;
        isLink = false;
      }
      entriesInfo.push({
        "permissions": permissions,
        "size": size,
        "date": date,
        "name": entryName,
        "isLink": isLink,
        "isFile": isFile
      });
    }
    return entriesInfo;
  };

  /**
  * getRemoteEntryNames - given the ls -l output, returns all the entry names 
  * @param {string} directoryListing The output from ls -l output
  * @return {array} the array containing the netry names
  */
  FTPUtils.getRemoteEntryNames = function(directoryListing) {
    var entries = FTPUtils.parseLsDirectoryListing(directoryListing);
    entryNames = [];
    for (var i =0; i < entries.length; i++) {
      entryNames.push(entries[i].name);
    }
    return entryNames;
  };

  FTPUtils.createFoldersAndGetFiles = function(remotePath, localDestination, size) {
    if (!FileSystemUtils.isFolder(remotePath)) {
      return {"filePath": remotePath, "destination": localDestination, "size": size};
    }
    else {
      var folderContent = [];
      return FileSystemUtils.createFolder(remotePath, localDestination).then(
        function (folderEntry) {
          return FTPUI.getRemoteDirEntriesInfo(remotePath).then(function (entriesInfo) {
            for (var i = 0; i < entriesInfo.length; i++) {
              var entryPath = remotePath + entriesInfo[i].name;
              var entrySize = entriesInfo[i].size;
              folderContent.push(FTPUtils.createFoldersAndGetFiles(entryPath, folderEntry, entrySize));
            }
            console.log("FOLDER CONTENT", folderContent);
            return Promise.all(folderContent);
          });
      });
    }
  };

  return FTPUtils;

} (FTPUtils || {}));

